from typing import Optional, Dict, Any, Self
from dataclasses import dataclass, field

from authx.core.enums import ReplacedRange, LogoutMode, LogoutRange
from authx.core.manager import AuthManager
from authx.dao.base import AuthTokenDao
from authx.settings.config import CookieConfig, AuthConfig


@dataclass
class LoginParameter:
    # * Cookie 配置对象
    cookie: CookieConfig
    # * 此次登录的客户端设备类型
    device_type: Optional[str] = field(default="default")
    # * 此次登录的客户端设备id
    device_id: Optional[str] = None
    # * 扩展信息（只在 jwt 模式下生效）
    extra_data: Optional[Dict[str, Any]] = None
    # * 预定Token（预定本次登录生成的Token值）
    token: Optional[str] = None
    # * 本次登录挂载到 SaTerminalInfo 的自定义扩展数据
    terminal_extra_data: Optional[Dict[str, Any]] = None
    # * 指定此次登录 token 有效期，单位：秒 （如未指定，自动取全局配置的 timeout 值）
    timeout: Optional[int] = None
    # * 指定此次登录 token 最低活跃频率，单位：秒（如未指定，则使用全局配置的 activeTimeout 值）
    active_timeout: Optional[int] = None
    # * 是否允许同一账号多地同时登录 （为 true 时允许一起登录, 为 false 时新登录挤掉旧登录）
    is_concurrent: Optional[bool] = None
    # * 在多人登录同一账号时，是否共用一个 token （为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token）
    is_share: Optional[bool] = None
    # * 同一账号最大登录数量，-1代表不限 （只有在 isConcurrent=true, isShare=false 时此配置项才有意义）
    max_login_count: Optional[int] = None
    # * 在每次创建 token 时的最高循环次数，用于保证 token 唯一性（-1=不循环尝试，直接使用）
    max_try_times: Optional[int] = None
    # * 是否为持久Cookie（临时Cookie在浏览器关闭时会自动删除，持久Cookie在重新打开后依然存在）
    is_lasting_cookie: Optional[bool] = None
    # * 是否在登录后将 Token 写入到响应头
    is_write_header: Optional[bool] = None
    # * 当 isConcurrent=false 时，顶人下线的范围 (CURR_DEVICE_TYPE=当前指定的设备类型端, ALL_DEVICE_TYPE=所有设备类型端)
    replaced_range: Optional[ReplacedRange] = None
    # * 溢出 maxLoginCount 的客户端，将以何种方式注销下线 (LOGOUT=注销下线, KICKOUT=踢人下线, REPLACED=顶人下线)
    # private SaLogoutMode overflowLogoutMode;
    overflow_logout_mode: Optional[LogoutMode] = None
    # * 在登录时，是否立即创建对应的 Token-Session （true=在登录时立即创建，false=在第一次调用 getTokenSession() 时创建）
    right_now_create_token_session: Optional[bool] = None

    @classmethod
    def from_config(cls, config: AuthConfig | None = None) -> Self:
        """
        基于全局/传入配置创建一个 LoginParameter 实例
        """
        config = config or AuthManager.get_config()
        return cls(
            cookie=config.cookie_config,
            device_type="default",
            timeout=config.timeout,
            active_timeout=config.active_timeout,
            is_concurrent=config.is_concurrent,
            is_share=config.is_share,
            max_login_count=config.max_login_count,
            max_try_times=config.max_try_times,
            is_lasting_cookie=config.is_lasting_cookie,
            is_write_header=config.is_write_header,
            replaced_range=config.replaced_range,
            overflow_logout_mode=config.logout_mode,
            right_now_create_token_session=config.is_right_now_create_token_session,
        )

    def set_extra_data(self, key: str, value: Any) -> "LoginParameter":
        """
        设置扩展数据
        """
        self.extra_data = self.extra_data or {}
        self.extra_data[key] = value
        return self

    # 支持多个扩展数据
    def set_extra_data_multi(self, **kwargs) -> "LoginParameter":
        """
        设置多个扩展数据
        """
        self.extra_data = self.extra_data or {}
        self.extra_data.update(kwargs)
        return self

    def get_extra_data(self, key: str) -> Any:
        """
        获取扩展数据
        """
        return self.extra_data.get(key)

    def has_extra_data(self) -> bool:
        """
        判断扩展数据是否存在
        """
        return bool(self.extra_data)

    # /**
    # 	 * 写入本次登录挂载到 SaTerminalInfo 的自定义扩展数据
    # 	 * @param key 键
    # 	 * @param value 值
    # 	 * @return 对象自身
    # 	 */

    def set_terminal_extra_data(self, key: str, value: Any) -> "LoginParameter":
        """
        写入本次登录挂载到 SaTerminalInfo 的自定义扩展数据
        """
        self.terminal_extra_data = self.terminal_extra_data or {}
        self.terminal_extra_data[key] = value
        return self

    def get_terminal_extra_data(self, key: str) -> Any:
        """
        获取本次登录挂载到 SaTerminalInfo 的自定义扩展数据
        """
        if not self.terminal_extra_data:
            return None
        return self.terminal_extra_data.get(key)

    def has_terminal_extra_data(self) -> bool:
        """
        判断本次登录挂载到 SaTerminalInfo 的自定义扩展数据是否存在
        """
        return bool(self.terminal_extra_data)

    def get_cookie_timeout(self) -> int:
        """
        计算 Cookie 时长
        """
        if not self.is_lasting_cookie:
            return -1
        _timeout = self.timeout
        if _timeout == AuthTokenDao.NEVER_EXPIRE or _timeout > 2147483647:
            return 2147483647
        return int(_timeout)


@dataclass
class LogoutParameter:
    # * 注销范围 (TOKEN=只注销当前 token 的会话，ACCOUNT=注销当前 token 指向的 loginId 其所有客户端会话)
    # * <br/> (此参数只在调用 StpUtil.logout(param) 时有效)
    range: LogoutRange
    # * 如果 token 已被冻结，是否保留其操作权 (是否允许此 token 调用注销API)
    # * <br/> (此参数只在调用 StpUtil.[logout/kickout/replaced]ByTokenValue("token", param) 时有效)
    is_keep_freeze_ops: bool = False
    # * 在注销 token 后，是否保留其对应的 Token-Session
    is_keep_token_session: bool = False
    # * 需要注销的设备类型 (为 null 代表不限制，为具体值代表只注销此设备类型的会话)
    # * <br/> (此参数只在调用 StpUtil.logout(id, parame) 时有效)
    device_type: str = "default"
    # * 需要注销的设备ID (为 null 代表不限制，为具体值代表只注销此设备ID的会话)
    # * <br/> (此参数只在调用 StpUtil.logout(id, param) 时有效)
    device_id: Optional[str] = None
    # * 注销类型 (LOGOUT=注销下线、KICKOUT=踢人下线，REPLACED=顶人下线)
    mode: LogoutMode = LogoutMode.LOGOUT

    @classmethod
    def from_config(cls, config: AuthConfig) -> Self:
        """
        基于配置创建一个 LogoutParameter 实例
        """
        return cls(
            range=config.logout_range,
            is_keep_freeze_ops=config.is_logout_keep_freeze_ops,
            is_keep_token_session=config.is_logout_keep_token_session,
        )

    def set_device_type(self, device_type: str) -> "LogoutParameter":
        self.device_type = device_type
        return self
